package org.clockworkmages.games.anno1186;

import java.awt.Image;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.Map.Entry;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.clockworkmages.games.anno1186.controllers.LocalMapMovementController;
import org.clockworkmages.games.anno1186.dao.FileUtil;
import org.clockworkmages.games.anno1186.gui.GamePanel;
import org.clockworkmages.games.anno1186.model.option.GoBackOption;
import org.clockworkmages.games.anno1186.model.option.Option;
import org.clockworkmages.games.anno1186.model.situation.Situation;
import org.clockworkmages.games.anno1186.scripting.EnemyScriptingWrapper;
import org.clockworkmages.games.anno1186.scripting.PlayerCharacterScriptingWrapper;
import org.clockworkmages.games.anno1186.scripting.PlotScriptingWrapper;
import org.clockworkmages.games.anno1186.scripting.ScriptingService;
import org.clockworkmages.games.anno1186.scripting.SituationScriptingWrapper;
import org.clockworkmages.games.anno1186.scripting.UtilScriptingWrapper;
import org.clockworkmages.games.anno1186.situations.gameoptions.GameMenuSituation;
import org.clockworkmages.games.anno1186.situations.gameoptions.options.GameMenuOption;
import org.clockworkmages.games.anno1186.situations.inventory.options.InventoryOption;

public class GameApplication {

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event-dispatching thread.
	 */
	public static void createAndShowGUI() {

		// Create and set up the window.
		JFrame frame = new JFrame("Anno 1186");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Container pane = frame.getContentPane();

		final GamePanel gamePanel = new GamePanel();
		frame.add(gamePanel);
		// Container pane = frame.getContentPane();

		// Display the window.
		frame.pack();
		frame.setVisible(true);

		// "Debug" for identifying problems observed when running the
		// application as a standalone JAR:
		Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
			public void uncaughtException(Thread t, Throwable e) {
				StringWriter errors = new StringWriter();
				e.printStackTrace(new PrintWriter(errors));
				gamePanel
						.getExplorationPanel()
						.getTextPane()
						.appendText(
								e.getClass().getName() + " " + e.getMessage()
										+ "\n\n" + errors.toString());
			}
		});

	}

	public static void initContextBeans() {

		GameBeansContext.register(new GameCharacterService());
		GameBeansContext.register(new GameDataService());
		GameBeansContext.register(new GameStateService());
		GameBeansContext.register(new GameTimeService());
		GameBeansContext.register(new SituationService());
		GameBeansContext.register(new SituationPoolService());
		GameBeansContext.register(new GameAreaService());

		GameBeansContext.register(new EnemyScriptingWrapper());
		GameBeansContext.register(new PlayerCharacterScriptingWrapper());
		GameBeansContext.register(new PlotScriptingWrapper());
		GameBeansContext.register(new ScriptingService());
		GameBeansContext.register(new SituationScriptingWrapper());
		GameBeansContext.register(new UtilScriptingWrapper());
		//
		GameBeansContext.register(InventoryOption.INSTANCE);
		GameBeansContext.register(GoBackOption.INSTANCE);
		GameBeansContext.register(GameMenuOption.INSTANCE);
		//
		GameBeansContext.register(GameMenuSituation.INSTANCE);

		GameGuiService gameGuiService = new GameGuiService();
		GameBeansContext.register(gameGuiService);

		LocalMapMovementController localMapMovementController = new LocalMapMovementController();
		GameBeansContext.register(localMapMovementController);

		GameBeansContext.processInjectAnnotations();
	}

	public static void initGameData() {

		// GameData gameData = new GameData();

		GameDataService gameDataService = GameBeansContext
				.getBean(GameDataService.class);
		String resourceListFile = "data/gameFiles.xml";
		gameDataService.mergeGameDataFromResourceList(resourceListFile);
		// GameDataManager
		// .mergeGameDataFromResource(gameData, "gameData/base.xml");
		// GameDataManager.mergeGameDataFromResource(gameData,
		// "gameData/characterCreation.xml");

		String gameDataLocalDirectory = "./data";
		gameDataService.mergeGameDataFromLocalDirectory(gameDataLocalDirectory);

		// Situation<?> gameStartSituation = gameData.getSituations().get(
		// gameData.getGameStartSituation());

	}

	public static void initTechnicalOptionsAndSituations() {
		GameDataService gameDataService = GameBeansContext
				.getBean(GameDataService.class);

		// InventoryOption inventoryOption = GameBeansContext
		// .getBean(InventoryOption.class);
		// GoBackOption goBackOption = GameBeansContext
		// .getBean(GoBackOption.class);

		for (Option option : new Option[] { InventoryOption.INSTANCE,
				GoBackOption.INSTANCE, GameMenuOption.INSTANCE }) {
			gameDataService.getGameData().getOptions()
					.put(option.getId(), option);
		}
		for (Situation situation : new Situation[] { GameMenuSituation.INSTANCE }) {
			gameDataService.getGameData().getSituations()
					.put(situation.getId(), situation);
		}

		gameDataService.getGameData().postMerge();

	}

	public static void initGuiResources() {
		GameDataService gameDataService = GameBeansContext
				.getBean(GameDataService.class);
		for (Entry<String, String> entry : gameDataService.getGameData()
				.getOptionImageIconDescriptors().entrySet()) {
			Image image = FileUtil.getResourceAsImage(entry.getValue());
			ImageIcon imageIcon = new ImageIcon(image, entry.getKey());
			gameDataService.getImageIcons().put(entry.getKey(), imageIcon);
		}
	}

	public static void startNewGame() {
		// Game.gameState = new GameState();

		GameStateService gameStateService = GameBeansContext
				.getBean(GameStateService.class);
		GameDataService gameDataService = GameBeansContext
				.getBean(GameDataService.class);
		GameTimeService gameTimeService = GameBeansContext
				.getBean(GameTimeService.class);
		SituationService situationService = GameBeansContext
				.getBean(SituationService.class);

		Calendar calendar = Calendar.getInstance();
		calendar.set(1492, Calendar.AUGUST, 2, 22, 16);

		gameStateService.getGameState().setTime(calendar.getTimeInMillis());
		gameTimeService.advanceTime(1L);

		Situation startSituation = gameDataService.getGameData()
				.getSituations().get(GameMenuSituation.ID);
		gameStateService.addSituation(startSituation, true);
		situationService.playCurrentSituation();

	}

	public static void main(String[] args) {
		try {
			// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
			UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
		} catch (UnsupportedLookAndFeelException ex) {
			ex.printStackTrace();
		} catch (IllegalAccessException ex) {
			ex.printStackTrace();
		} catch (InstantiationException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		/* Turn off metal's use bold fonts */
		UIManager.put("swing.boldMetal", Boolean.FALSE);

		// Schedule a job for the event dispatch thread:
		// creating and showing this application's GUI.
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				initContextBeans();
				createAndShowGUI();
				initGameData();
				initTechnicalOptionsAndSituations();
				initGuiResources();
				startNewGame();
			}
		});
	}

}
